#include <EEPROM.h>
#include <map>
#include <vector>
#include <limits>
// #include <shared_mutex>
#define CONFIG_MAGIC_NUM 0xFFEE
// 因为ESP32不直接使用flash模拟EEPROM了，而使用了nvs,nvs可以简单的看做一个有文件名的文件.
// 那么这样要想模拟EEPROM就必须固定大小访问，否则就可能会出现数据错误
// 8266的库begin返回的void,ESP32返回的是bool
#ifdef USE_8266
#define CHECK_EERPOM_BEGIN(val) (val), true
#else
#define CHECK_EERPOM_BEGIN(val) val
#endif

#define CONFIG_SIZE 512

// 保存和读取配置
class CConfigBase
{
public:

    using DataSizeType = uint8_t;

    using DataIDType = uint8_t;

private:
    bool m_bMotify = false;

    using DataMap = std::map<DataIDType, std::vector<char>>;

    DataMap m_lsData;

    // std::shared_mutex mutex_;

    void GetData(DataMap &out, uint8_t **psrc, DataSizeType max_size = std::numeric_limits<DataSizeType>::max())
    {
        DataSizeType dataSize = (*psrc)[0];
        (*psrc)++;
        if (dataSize > sizeof(DataIDType) && dataSize <= max_size)
        {
            DataIDType id;
            memcpy(&id, (*psrc), sizeof(DataIDType));
            std::vector<char> &idContent = out[id];
            idContent.resize(dataSize - sizeof(DataIDType));
            memcpy(idContent.data(), (*psrc) + sizeof(DataIDType), dataSize - sizeof(DataIDType));
            (*psrc) += dataSize;
        }
    }

    void PutData(const DataIDType id, const std::vector<char> &data, uint8_t **pdata)
    {
        ((DataSizeType *)(*pdata))[0] = (DataSizeType)data.size() + sizeof(DataIDType);
        *pdata += sizeof(DataSizeType);
        memcpy(*pdata, &id, sizeof(DataIDType));
        *pdata += sizeof(DataIDType);
        memcpy(*pdata, data.data(), data.size());
        *pdata += data.size();
    }

    void PrintEEPROM(const uint8_t *pEEPROM, const uint8_t *pEnd)
    {
        for (; pEEPROM < pEnd;)
        {
            DataSizeType dataSize = pEEPROM[0];
            pEEPROM += sizeof(DataSizeType);
            char *pout = new char[dataSize + 1];
            memcpy(pout, pEEPROM, dataSize);
            pout[dataSize] = '\0';
            Serial.println(dataSize);
            Serial.println(pout);
            delete[] pout;
            pEEPROM += dataSize;
        }
    }

    uint16_t GetDataSize() const
    {
        // size+id固定长度
        uint16_t needsize = m_lsData.size() * (sizeof(DataSizeType) + sizeof(DataIDType));
        for (auto const &ite : m_lsData)
        {
            needsize += ite.second.size(); // content
        }
        return needsize;
    }

    // 从EEPROM读取配置文件addr为保存配置文件地址
    bool init()
    {
        /*
        uint16_t   uint16_t     content
        magic_num  config_size  id--data
        */
        Serial.println(u8"开始读取配置文件");

        uint16_t config_size = 0;
        if (CHECK_EERPOM_BEGIN(EEPROM.begin(CONFIG_SIZE)))
        {
            uint16_t magic_num = 0;
            if (EEPROM.get(0, magic_num) != CONFIG_MAGIC_NUM)
            {
                Serial.println(u8"非法的配置文件");
                return false;
            }
            // 正常的配置文件读取配置文件尺寸
            EEPROM.get(sizeof(uint16_t), config_size);

            // 真实尺寸
            if (config_size > 0)
            {
                uint8_t *pCur = EEPROM.getDataPtr() + sizeof(uint16_t) * 2;
                uint8_t *pEnd = pCur + config_size;
                while (pCur < pEnd)
                {
                    GetData(m_lsData, &pCur, pEnd - pCur);
                }
                EEPROM.end();

                return true;
            }
        }
        return false;
    }

public:

    CConfigBase()
    {
        // std::unique_lock<std::shared_mutex> lock(mutex_);
        init();
    }

    ~CConfigBase()
    {
        if (m_bMotify)
        {
            SaveConfig();
        }
    }

    bool IsValid()const
    {
        return !m_lsData.empty();
    }

    void Clear()
    {
        m_lsData.clear();
    }

    void Remove(DataIDType id)
    {
        auto ite = m_lsData.find(id);
        if (ite != m_lsData.end())
        {
            m_lsData.erase(ite);
        }
    }

    bool GetData(DataIDType id, std::vector<char> &out) const
    {
        // std::shared_lock<std::shared_mutex> lock(mutex_);
        auto ite = m_lsData.find(id);
        if (ite != m_lsData.end())
        {
            out = ite->second;
            return true;
        }
        return false;
    }

    template <typename retType>
    retType GetData(DataIDType id) const
    {
        // std::shared_lock<std::shared_mutex> lock(mutex_);
        retType ret{};
        auto ite = m_lsData.find(id);
        if (ite != m_lsData.end())
        {
            ret = *(retType *)(ite->second.data());
        }
        return ret;
    }

    String &GetData(DataIDType id, String &out) const
    {
        // std::shared_lock<std::shared_mutex> lock(mutex_);
        auto ite = m_lsData.find(id);
        if (ite != m_lsData.end())
        {
            out.concat(ite->second.data(), ite->second.size());
        }
        return out;
    }

    void SetData(DataIDType id, std::vector<char> &&content)
    {
        // std::unique_lock<std::shared_mutex> lock(mutex_);
        m_lsData[id] = content;
        m_bMotify = true;
    }

    template <typename setType>
    void SetData(DataIDType id, setType content)
    {
        std::vector<char> vecContent;
        vecContent.resize(sizeof(setType));
        memcpy(vecContent.data(), &content, sizeof(setType));
        // std::unique_lock<std::shared_mutex> lock(mutex_);
        m_lsData[id] = vecContent;
        m_bMotify = true;
    }

    void SetData(DataIDType id, std::vector<char> &content)
    {
        // std::unique_lock<std::shared_mutex> lock(mutex_);
        m_lsData[id] = content;
        m_bMotify = true;
    }

    void SetData(DataIDType id, const char *content, size_t len)
    {
        // std::unique_lock<std::shared_mutex> lock(mutex_);
        if (len > 0)
        {
            m_lsData[id].resize(len);
            memcpy(m_lsData[id].data(), content, len);
            m_bMotify = true;
        }
        else if (len == 0)
        {
            m_lsData[id].clear();
            m_bMotify = true;
        }
    }

    void SaveConfig()
    {
        // std::shared_lock<std::shared_mutex> lock(mutex_);
        uint16_t needsize = GetDataSize();
        uint16_t magic_num = CONFIG_MAGIC_NUM;

        if (CHECK_EERPOM_BEGIN(EEPROM.begin(CONFIG_SIZE)))
        {
            EEPROM.put(0, magic_num);
            EEPROM.put(sizeof(uint16_t), needsize);

            uint8_t *pCur = EEPROM.getDataPtr();
            pCur += sizeof(uint16_t) * 2;

            int freeSize = CONFIG_SIZE - sizeof(uint16_t) * 2;
            for (auto const &ite : m_lsData)
            {
                // 没有空间保存了
                if ((freeSize -= (int)sizeof(DataIDType)) < (int)ite.second.size())
                    break;
                PutData(ite.first, ite.second, &pCur);
                freeSize -= ite.second.size();
            }
            EEPROM.end();
        }
        m_bMotify=false;
    }
};